﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using NAudio;
using NAudio.Wave;

using System.IO;
using System.Threading;
using System.Web.Script.Serialization;

namespace GA_Chiptunes
{
    public partial class Form1 : Form
    {
        string fileName;

        List<Organism> population = new List<Organism>();
        List<Organism> breedingStock = new List<Organism>();

        WaveTone tone = new WaveTone(100, 0.1);
        DirectSoundOut output = new DirectSoundOut();
        BlockAlignReductionStream stream;

        static Random number = new Random();
        int populationSize = 8;
        int mutationRate = 16;

        int maxFrequency = 500;
        int maxAmplitude = 1000;
        int maxDuration = 500;

        public Form1()
        {
            InitializeComponent();
            for (int i = 0; i < populationSize; i++)
            {
                population.Add(new Organism(4, 4, maxFrequency,
                    maxAmplitude, maxDuration));
                comboBox1.Items.Add("Organism " + i);
            }

            textBox1.Text = Convert.ToString(maxFrequency);
            textBox2.Text = Convert.ToString(maxAmplitude);
            textBox3.Text = Convert.ToString(maxDuration);
            textBox4.Text = Convert.ToString(populationSize);
            textBox5.Text = Convert.ToString(mutationRate);
        }

        private void Test()
        {
            Organism test = new Organism(4, 4, maxFrequency,
                maxAmplitude, maxDuration);
            test.DisplayOrganism();
            test.DisplayParameters();
            //test.maxDuration = 10000;
            for (int i = 0; i < 100; i++)
            {
                test.Mutate();
            }
            test.DisplayOrganism();
            test.DisplayParameters();
        }

        private Organism Breed(Organism mother, Organism father)
        {
            Organism child = new Organism();
            int decision;
            Organism longer;
            Organism shorter;

            if (mother.length >= father.length)
            {
                longer = new Organism(mother);
                shorter = new Organism(father);
            }
            else
            {
                longer = new Organism(father);
                shorter = new Organism(mother);
            }

            for (int i = 0; i < longer.length; i++)
            {
                decision = number.Next(0, 2);
                if (i < shorter.length)
                {
                    if (decision == 0)
                    {
                        child.Genotype.Add(longer.Genotype[i]);
                    }
                    else
                    {
                        child.Genotype.Add(shorter.Genotype[i]);
                    }
                }
                else
                {
                    child.InsertChromosome(longer.Genotype[i]);
                }
            }
            child.length = child.Genotype.Count;

            return child;
        }

        private void Evolve()
        {
            List<Organism> newPopulation = new List<Organism>();
            int mother;
            int father;
            int selection;

            //population[0].DisplayOrganism();

            population.Clear();
            comboBox1.Items.Clear();
            for (int i = 0; i < populationSize; i++)
            {
                mother = number.Next(0, breedingStock.Count);
                father = number.Next(0, breedingStock.Count);
                newPopulation.Add(Breed(breedingStock[mother], breedingStock[father]));
            }

            for (int i = 0; i < newPopulation.Count; i++)
            {
                selection = number.Next(0, newPopulation.Count);
                newPopulation[selection].Mutate();
            }

            population = newPopulation;
            Application.Idle += new EventHandler(updateButton_Click);
            //population[0].DisplayOrganism();
        }

        private void samplePlayButton_Click(object sender, EventArgs e)
        {
            try
            {
                int index = comboBox1.SelectedIndex;
                Thread play = new Thread(() =>
                    population[index].PlayOrganism());
                play.Start();
            }
            catch (ArgumentOutOfRangeException err)
            {
                MessageBox.Show("Not a valid selection.");
                return;
            }
        }

        private void selectButton_Click(object sender, EventArgs e)
        {
            try
            {
                breedingStock.Add(population[comboBox1.SelectedIndex]);
                comboBox2.Items.Add("Organism " + comboBox1.SelectedIndex);
            }
            catch (ArgumentOutOfRangeException err)
            {
                MessageBox.Show("Not a valid selection.");
                return;
            }
        }

        private void removeButton_Click(object sender, EventArgs e)
        {
            try
            {
                breedingStock.Remove(population[comboBox1.SelectedIndex]);
                comboBox2.Items.Remove("Organism " + comboBox1.SelectedIndex);
                comboBox2.Text = "";
            }
            catch (ArgumentOutOfRangeException err)
            {
                MessageBox.Show("Not a valid selection.");
                return;
            }
        }

        private void breedButton_Click(object sender, EventArgs e)
        {
            if (breedingStock.Count < 1)
            {
                MessageBox.Show("There must be at least one member in the breeding stock.");
                return;
            }

            Evolve();
            breedingStock.Clear();
            comboBox2.Items.Clear();

            for (int i = 0; i < populationSize; i++)
            {
                comboBox1.Items.Add("Organism " + i);
            }
        }

        private void updateButton_Click(object sender, EventArgs e)
        {
            if (!int.TryParse(textBox1.Text, out maxFrequency))
            {
                MessageBox.Show("Please enter a valid maximum frequency.");
            }

            if (!int.TryParse(textBox2.Text, out maxAmplitude))
            {
                MessageBox.Show("Please enter a valid maximum amplitude.");
            }

            if (!int.TryParse(textBox3.Text, out maxDuration))
            {
                MessageBox.Show("Please enter a valid maximum duration.");
            }

            if (!int.TryParse(textBox4.Text, out populationSize))
            {
                MessageBox.Show("Please enter a valid population size.");
            }

            if (!int.TryParse(textBox5.Text, out mutationRate))
            {
                MessageBox.Show("Please enter a valid maximum mutation.");
            }

            for (int i = 0; i < population.Count; i++)
            {
                population[i].maxFrequency = maxFrequency;
                population[i].maxAmplitude = maxAmplitude;
                population[i].maxDuration = maxDuration;
                population[i].mutationRate = mutationRate;
            }

            //Test();
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            fileName = "";

            population.Clear();
            breedingStock.Clear();
            comboBox1.Items.Clear();
            comboBox2.Items.Clear();

            populationSize = 8;
            mutationRate = 16;

            maxFrequency = 500;
            maxAmplitude = 1000;
            maxDuration = 500;

            for (int i = 0; i < populationSize; i++)
            {
                population.Add(new Organism(4, 4, maxFrequency,
                    maxAmplitude, maxDuration));
                comboBox1.Items.Add("Organism " + i);
            }

            textBox1.Text = Convert.ToString(maxFrequency);
            textBox2.Text = Convert.ToString(maxAmplitude);
            textBox3.Text = Convert.ToString(maxDuration);
            textBox4.Text = Convert.ToString(populationSize);
            textBox5.Text = Convert.ToString(mutationRate);
        }

        private void openOrganismToolStripMenuItem_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "JSON (*.json)|*.json;";
            DialogResult dr = openFileDialog1.ShowDialog();
            if (dr == DialogResult.OK)
            {
                fileName = openFileDialog1.FileName;

                JavaScriptSerializer serializer = new JavaScriptSerializer();
                string tempOrg = File.ReadAllText(fileName);
                Organism newOrg = serializer.Deserialize<Organism>(tempOrg);
                population.Add(newOrg);
                comboBox1.Items.Add("Organism " + comboBox1.Items.Count);
            }
        }

        private void openPopulationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "JSON (*.json)|*.json;";
            DialogResult dr = openFileDialog1.ShowDialog();
            if (dr == DialogResult.OK)
            {
                fileName = openFileDialog1.FileName;

                population.Clear();
                breedingStock.Clear();
                comboBox1.Items.Clear();
                comboBox2.Items.Clear();

                JavaScriptSerializer serializer = new JavaScriptSerializer();
                string tempPop = File.ReadAllText(fileName);
                List<Organism> newPop = serializer.Deserialize<List<Organism>>(tempPop);

                Console.WriteLine(tempPop);

                //Console.WriteLine(newPop.ToString());
                //for (int i = 0; i < population.Count; i++)
                //{
                //    population.Add(newPop[i]);
                //    newPop[i].DisplayOrganism();
                //    comboBox1.Items.Add("Organism " + i);
                //}
            }
        }

        private void saveOrganismToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                int index = comboBox1.SelectedIndex;
                string jsonOrganism = serializer.Serialize(population[index]);

                saveFileDialog1.Filter = "JSON (*.json)|*.json;";
                DialogResult dr = saveFileDialog1.ShowDialog();
                if (dr == DialogResult.OK)
                {
                    fileName = saveFileDialog1.FileName;
                    File.WriteAllText(fileName, jsonOrganism);
                }
            }
            catch
            {
                MessageBox.Show("Not a valid selection.");
                return;
            }
        }

        private void savePopulationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            string jsonPopulation = serializer.Serialize(population);

            saveFileDialog1.Filter = "JSON (*.json)|*.json;";
            DialogResult dr = saveFileDialog1.ShowDialog();
            if (dr == DialogResult.OK)
            {
                fileName = saveFileDialog1.FileName;
                File.AppendAllText(fileName, jsonPopulation);
            }
        }

        private void saveOrganismToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            if (fileName == null || fileName == "")
            {
                saveOrganismToolStripMenuItem_Click(sender, e);
            }
            else
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                int index = comboBox1.SelectedIndex;
                string jsonOrganism = serializer.Serialize(population[index]);

                File.WriteAllText(fileName, jsonOrganism);
            }
        }

        private void savePopulationToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            if (fileName == null || fileName == "")
            {
                savePopulationToolStripMenuItem_Click(sender, e);
            }
            else
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                string jsonPopulation = serializer.Serialize(population);

                File.AppendAllText(fileName, jsonPopulation);
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void displayButton_Click(object sender, EventArgs e)
        {
            try
            {
                population[comboBox1.SelectedIndex].DisplayOrganism();
            }
            catch (ArgumentOutOfRangeException err)
            {
                MessageBox.Show("Not a valid selection.");
                return;
            }
        }
    }
}
